using System.Linq;
using System.Reflection;
using UnityEngine;
using System.Collections.Generic;
using Aliyun.OSS;

namespace cframework
{
    public enum ToolType
    {
        cmd,
        python,
    }

    public class EssentialToolInfo
    {
        public string[] toolNames;
        public string downloadUrl;
        public ToolType type;
        public EssentialToolInfo parent;

        public EssentialToolInfo(string[] _toolNames, string downloadUrl = null, ToolType type = ToolType.cmd, EssentialToolInfo parent = null)
        {
            toolNames = _toolNames;
            this.downloadUrl = downloadUrl;
            this.type = type;
            this.parent = parent;
        }

        public EssentialToolInfo(string _toolName, string downloadUrl = null, ToolType type = ToolType.cmd, EssentialToolInfo parent = null)
            : this(new string[] { _toolName }, downloadUrl, type, parent)
        {
        }

        public (string, string, string) Check(DeviceToolPathSetting toolPathsSetting)
        {
            if (this.parent != null)
            {
                var ret = this.parent.Check(toolPathsSetting);
                if (!string.IsNullOrEmpty(ret.Item1))
                {
                    return ret;
                }
            }

            var f = this;
            bool foundTool = false;
            foreach (var t in f.toolNames)
            {
                switch(type)
                {
                    case ToolType.cmd:
                        {
                            var toolPath = toolPathsSetting.GetCmdToolPath(t);
                            if (string.IsNullOrEmpty(toolPath))
                            {
                                //return (t, null, f.downloadUrl);
                            }
                            else if (!System.IO.File.Exists(toolPath))
                            {
                                //return (t, toolPath, null);
                            }
                            else
                            {
                                foundTool = true;
                            }
                        }
                        break;
                    case ToolType.python:
                        {
                            if (CheckToolHelper.CheckPythonTool(t))
                            {
                                foundTool = true;
                            }
                            else
                            {

                            }
                        }
                        break;

            }
        }

            if (!foundTool)
            {
                return (string.Join(',', f.toolNames), null, f.downloadUrl);
            }

            return (null, null, null);
        }


        //public static (string, string, string) Check(ScriptableObject o, DeviceToolPathSetting toolPathsSetting)
        //{
        //    var attrs = o.GetType().GetCustomAttributes<EssentialToolsAttribute>(true);
        //    foreach (var f in attrs)
        //    {
        //        var ret = f.Check(toolPathsSetting);
        //        if (!string.IsNullOrEmpty(ret.Item1))
        //        {
        //            return ret;
        //        }
        //    }

        //    var fieldInfos = o.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance | BindingFlags.FlattenHierarchy);
        //    foreach (var f in fieldInfos)
        //    {
        //        if (f.FieldType == typeof(ScriptableObject) || f.FieldType.IsSubclassOf(typeof(ScriptableObject)))
        //        {
        //            var v = f.GetValue(o);
        //            var ret = Check(v as ScriptableObject, toolPathsSetting);
        //            if (ret.Item1 != null)
        //            {
        //                return ret;
        //            }
        //        }
        //    }

        //    return (null, null, null);
        //}

        //public static void GetAllToolPaths(ScriptableObject o, DeviceToolPathSetting toolPathsSetting, string fieldPath, List<(string, string, string)> results)
        //{
        //    if (o == null)
        //    {
        //        return;
        //    }

        //    if (fieldPath == null)
        //    {
        //        fieldPath = o.name;
        //    }

        //    var attrs = o.GetType().GetCustomAttributes<EssentialToolsAttribute>(true);

        //    foreach (var f in attrs)
        //    {
        //        foreach (var t in f.toolNames)
        //        {
        //            var toolPath = toolPathsSetting.GetCmdToolPath(t);
        //            if (!string.IsNullOrEmpty(toolPath) && System.IO.File.Exists(toolPath))
        //            {
        //                results.Add((t, toolPath, fieldPath));
        //            }
        //            else
        //            {
        //                results.Add((t, f.downloadUrl, fieldPath));
        //            }
        //        }
        //    }

        //    var fieldInfos = o.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance | BindingFlags.FlattenHierarchy);
        //    foreach (var f in fieldInfos)
        //    {
        //        if (f.FieldType == typeof(ScriptableObject) || f.FieldType.IsSubclassOf(typeof(ScriptableObject)))
        //        {
        //            var v = f.GetValue(o);

        //            GetAllToolPaths(v as ScriptableObject, toolPathsSetting, fieldPath + "." + f.Name, results);
        //        }
        //    }
        //}
    }


    [System.AttributeUsage(System.AttributeTargets.All, AllowMultiple = true, Inherited = true)]
    public class EssentialToolAttribute : System.Attribute
    {
        

        EssentialToolInfo essentialTool;

        public EssentialToolAttribute(string[] _toolNames, string downloadUrl = null, ToolType tooltype = ToolType.cmd)
        {
            essentialTool = new EssentialToolInfo(_toolNames, downloadUrl, tooltype, null);
        }

        public EssentialToolAttribute(string _toolName, string downloadUrl = null, ToolType tooltype = ToolType.cmd)
            : this(new string[] { _toolName }, downloadUrl, tooltype)
        {
        }

        //public EssentialToolAttribute(string[] _toolNames, string downloadUrl, EssentialToolInfo parent = null)
        //{
        //    essentialTool = new EssentialToolInfo(_toolNames, downloadUrl, parent);
        //}

        //public EssentialToolAttribute(string _toolName, string downloadUrl, EssentialToolInfo parent = null)
        //    : this(new string[] { _toolName }, downloadUrl, parent)
        //{
        //}



        public static (string, string, string) Check(ScriptableObject o, DeviceToolPathSetting toolPathsSetting)
        {
            var attrs = o.GetType().GetCustomAttributes<EssentialToolAttribute>(true);
            foreach (var f in attrs)
            {
                var ret = f.essentialTool.Check(toolPathsSetting);
                if(!string.IsNullOrEmpty(ret.Item1))
                {
                    return ret;
                }
            }

            var fieldInfos = o.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance | BindingFlags.FlattenHierarchy);
            foreach (var f in fieldInfos)
            {
                if (f.FieldType == typeof(ScriptableObject) || f.FieldType.IsSubclassOf(typeof(ScriptableObject)))
                {
                    var v = f.GetValue(o);
                    var ret = Check(v as ScriptableObject, toolPathsSetting);
                    if (ret.Item1 != null)
                    {
                        return ret;
                    }
                }
            }

            return (null, null, null);
        }

        public static void GetAllToolPaths(ScriptableObject o, DeviceToolPathSetting toolPathsSetting, string fieldPath, List<(string, string, string)> results)
        {
            if(o == null)
            {
                return;
            }

            if(fieldPath == null)
            {
                fieldPath = o.name;
            }

            var attrs = o.GetType().GetCustomAttributes<EssentialToolAttribute>(true);
            
            foreach (var f in attrs)
            {
                foreach (var t in f.essentialTool.toolNames)
                {
                    var toolPath = toolPathsSetting.GetCmdToolPath(t);
                    if (!string.IsNullOrEmpty(toolPath) && System.IO.File.Exists(toolPath))
                    {
                        results.Add((t, toolPath, fieldPath));
                    }
                    else
                    {
                        results.Add((t, f.essentialTool.downloadUrl, fieldPath));
                    }
                }
            }

            var fieldInfos = o.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance | BindingFlags.FlattenHierarchy);
            foreach (var f in fieldInfos)
            {
                if (f.FieldType == typeof(ScriptableObject) || f.FieldType.IsSubclassOf(typeof(ScriptableObject)))
                {
                    var v = f.GetValue(o);

                    GetAllToolPaths(v as ScriptableObject, toolPathsSetting, fieldPath + "." + f.Name, results);
                }
            }
        }
    }


    [System.AttributeUsage(System.AttributeTargets.Field | System.AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
    public class EssentialAttribute : System.Attribute
    {
        object invalidValue;
        string link;

        public EssentialAttribute(object _invalidValue = null, string link = null)
        {
            invalidValue = _invalidValue;
        }

        public static List<string> Check(object o, string parentFields = null, System.Collections.Generic.List<string> needFields = null)
        {
            if (o == null)
            {
                return null;
            }

            if (needFields == null)
            {
                needFields = new List<string>();
            }

            var fieldInfos = o.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance | BindingFlags.FlattenHierarchy);

            foreach (var f in fieldInfos)
            {
                var attr = f.GetCustomAttribute<EssentialAttribute>();
                if (attr != null)
                {
                    if (f.FieldType == typeof(string))
                    {
                        if (string.IsNullOrEmpty((string)f.GetValue(o)))
                        {
                            needFields.Add(parentFields + "." + f.Name);
                        }
                    }
                    else if (f.FieldType == typeof(int))
                    {
                        if (0 == (int)f.GetValue(o))
                        {
                            needFields.Add(parentFields + "." + f.Name);
                        }
                    }
                    else if (!f.FieldType.IsValueType)
                    {
                        if (f.GetValue(o) == attr.invalidValue)
                        {
                            needFields.Add(parentFields + "." + f.Name);
                        }
                    }
                    else if (f.FieldType.IsEnum)
                    {
                        if (f.GetValue(o) == attr.invalidValue)
                        {
                            needFields.Add(parentFields + "." + f.Name);
                        }
                    }
                    else
                    {
                        throw new System.Exception("");
                    }
                }

                if (f.FieldType == typeof(object) || f.FieldType.IsSubclassOf(typeof(object)))
                {
                    var oo = f.GetValue(o);
                    Check(oo, parentFields + "." + f.Name, needFields);
                }
            }

            return needFields;
        }
    }
}